From ff9575f410013bbb68491d5150304b6fdad9bfbb Mon Sep 17 00:00:00 2001 From: "kaf24@scramble.cl.cam.ac.uk" Date: Thu, 5 Feb 2004 15:24:04 +0000 Subject: [PATCH] bitkeeper revision 1.705 (40226014Y-RJhXC4XKyIIvcyJtSHuQ) memory.c, domain.c: Extend support for linear page tables by allowing page directories to map one another. --- xen/common/domain.c | 19 ++++++++++++++++++ xen/common/memory.c | 47 ++++++++++++++++++++++++++++++++++++++------- 2 files changed, 59 insertions(+), 7 deletions(-) diff --git a/xen/common/domain.c b/xen/common/domain.c index 8e3338f7b5..139ff3742d 100644 --- a/xen/common/domain.c +++ b/xen/common/domain.c @@ -343,6 +343,7 @@ void free_all_dom_mem(struct task_struct *p) { struct list_head *ent, zombies; struct pfn_info *page; + unsigned long x, y; INIT_LIST_HEAD(&zombies); @@ -394,6 +395,24 @@ void free_all_dom_mem(struct task_struct *p) if ( test_and_clear_bit(_PGC_allocated, &page->count_and_flags) ) put_page(page); + /* + * Forcibly invalidate L2 tables at this point to break circular + * 'linear page table' references. This is okay because MMU structures + * are not shared across domains and this domain is now dead. Thus L2 + * tables are not in use so a non-zero count means circular reference. + */ + y = page->type_and_flags; + do { + x = y; + if ( likely((x & (PGT_type_mask|PGT_validated)) != + (PGT_l2_page_table|PGT_validated)) ) + break; + y = cmpxchg(&page->type_and_flags, x, x & ~PGT_validated); + if ( likely(y == x) ) + free_page_type(page, PGT_l2_page_table); + } + while ( unlikely(y != x) ); + put_page(page); } } diff --git a/xen/common/memory.c b/xen/common/memory.c index aeddc3ffe2..4d3201706c 100644 --- a/xen/common/memory.c +++ b/xen/common/memory.c @@ -346,11 +346,22 @@ static int get_page_and_type_from_pagenr(unsigned long page_nr, /* - * We allow an L2 table to map itself, to achieve a linear p.t. Note that this - * does not raise any reference counts. + * We allow an L2 tables to map each other (a.k.a. linear page tables). It + * needs some special care with reference counst and access permissions: + * 1. The mapping entry must be read-only, or the guest may get write access + * to its own PTEs. + * 2. We must only bump the reference counts for an *already validated* + * L2 table, or we can end up in a deadlock in get_page_type() by waiting + * on a validation that is required to complete that validation. + * 3. We only need to increment the reference counts for the mapped page + * frame if it is mapped by a different L2 table. This is sufficient and + * also necessary to allow validation of an L2 table mapping itself. */ -static int check_linear_pagetable(l2_pgentry_t l2e, unsigned long pfn) +static int get_linear_pagetable(l2_pgentry_t l2e, unsigned long pfn) { + unsigned long x, y; + struct pfn_info *page; + if ( (l2_pgentry_val(l2e) & _PAGE_RW) ) { MEM_LOG("Attempt to create linear p.t. with write perms"); @@ -359,8 +370,27 @@ static int check_linear_pagetable(l2_pgentry_t l2e, unsigned long pfn) if ( (l2_pgentry_val(l2e) >> PAGE_SHIFT) != pfn ) { - MEM_LOG("L2 tables may not map _other_ L2 tables!\n"); - return 0; + /* Make sure the mapped frame belongs to the correct domain. */ + if ( unlikely(!get_page_from_pagenr(l2_pgentry_to_pagenr(l2e))) ) + return 0; + + /* + * Make sure that the mapped frame is an already-validated L2 table. + * If so, atomically increment the count (checking for overflow). + */ + page = &frame_table[l2_pgentry_to_pagenr(l2e)]; + y = page->type_and_flags; + do { + x = y; + if ( unlikely((x & PGT_count_mask) == PGT_count_mask) || + unlikely((x & (PGT_type_mask|PGT_validated)) != + (PGT_l2_page_table|PGT_validated)) ) + { + put_page(page); + return 0; + } + } + while ( (y = cmpxchg(&page->type_and_flags, x, x + 1)) != x ); } return 1; @@ -406,7 +436,7 @@ static int get_page_from_l2e(l2_pgentry_t l2e, unsigned long pfn) if ( unlikely(!get_page_and_type_from_pagenr( l2_pgentry_to_pagenr(l2e), PGT_l1_page_table)) ) - return check_linear_pagetable(l2e, pfn); + return get_linear_pagetable(l2e, pfn); return 1; } @@ -434,7 +464,10 @@ static void put_page_from_l1e(l1_pgentry_t l1e) } -/* NB. Virtual address 'l2e' maps to a machine address within frame 'pfn'. */ +/* + * NB. Virtual address 'l2e' maps to a machine address within frame 'pfn'. + * Note also that this automatically deals correctly with linear p.t.'s. + */ static void put_page_from_l2e(l2_pgentry_t l2e, unsigned long pfn) { ASSERT(l2_pgentry_val(l2e) & _PAGE_PRESENT); -- 2.30.2